package com.privasphere.wikidoclet.dataTransport;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadCoordinator {
	public static final int PAGE_WORKLOAD_INIT_VAL_PER_THREAD = 3;
	private static final Logger LOGGER = LoggerFactory.getLogger(ThreadCoordinator.class.getName());
	
	private List<WikiDocPageData> myNewWikiDocPages;
	private Map<String, WikiDocMetaData> oldWikiDocPagesMetaDatas = null;
	private WikiTransporterJWBF wikiTransporterPerformanceOpt;
	private String wikiDocIndexPageIdentifier;
	private ThreadPool myThreadPool;
	private int threadCounter = 0;
	

	public ThreadCoordinator() {
		LOGGER.trace("ThreadCoordinater started");
		XmlReadeable<WikiDocPageData> myXmlParser = XmlReaderFactory.getXmlParser(ParserTypeEnum.SAX_PARSER);
		myNewWikiDocPages = myXmlParser.getParsedElements();
		wikiDocIndexPageIdentifier = myXmlParser.getIndexPageIdentifier();
		wikiTransporterPerformanceOpt = new WikiTransporterJWBF();
		wikiTransporterPerformanceOpt.initCommunicationChannel();
		
		myThreadPool = new ThreadPool();
	}
	
	/**
	 * 
	 * @param oldWikiDocPagesMetaDatas
	 */
	public synchronized void setOldWikiPagesInfo(Map<String, WikiDocMetaData> oldWikiDocPagesMetaDatas){
		if(this.oldWikiDocPagesMetaDatas == null){
			this.oldWikiDocPagesMetaDatas = oldWikiDocPagesMetaDatas;
		}
		else{
			this.oldWikiDocPagesMetaDatas.putAll(oldWikiDocPagesMetaDatas);
		}
	}
	
	public synchronized void threadFinished() {
		threadCounter--;
		notifyAll();
	}

	public void startCoordinating(){
		int counter = 0;
		
		List<WikiDocPageData> workLoad = new LinkedList<WikiDocPageData>();
		HashMap<String, WikiDocMetaData> wikiDocPagesMetaData = wikiTransporterPerformanceOpt.getWikiPagesMetaData(wikiDocIndexPageIdentifier);
		WikiDocPageData pageData;
		
		LOGGER.trace("IndexPageIDENTIFIER: " + this.wikiDocIndexPageIdentifier);
		
		for(Iterator<WikiDocPageData> pageDatasIt = myNewWikiDocPages.iterator(); pageDatasIt.hasNext();){
			
			pageData = pageDatasIt.next();
			LOGGER.trace(pageData.getTitleValue());
			workLoad.add(pageData);
			pageDatasIt.remove();			
			
			if (counter++ == PAGE_WORKLOAD_INIT_VAL_PER_THREAD || myNewWikiDocPages.isEmpty()) {
				myThreadPool.startServing(new WikiTransporterThread(new WikiTransporterJWBF(workLoad, wikiDocPagesMetaData, this)));
				threadCounter++;
				workLoad = new LinkedList<WikiDocPageData>();
				counter = 0;
			}
		}
		
		while (threadCounter > 0){
			try {
				synchronized(this){
					this.wait();
				}
			} catch (InterruptedException e) {
				LOGGER.error(e.getMessage());
			}
		}
		
		if(oldWikiDocPagesMetaDatas != null){
			wikiTransporterPerformanceOpt.removeOldWikiDocPages(oldWikiDocPagesMetaDatas);
		}
		
		LOGGER.trace("ThreadCoordinater ended.");
	}
	
	private class ThreadPool {
		private ExecutorService threadPool;

		public ThreadPool() {
			initializeThreadPool();
		}

		private void initializeThreadPool() {
			threadPool = Executors.newFixedThreadPool(2);
		}
		
		public void startServing(Runnable t){
			threadPool.execute(t);
			LOGGER.debug("ThreadPool: New thread executed!");
		}

	}
	
	
	private class WikiTransporterThread implements Runnable {
		Transportable t;
		
		public WikiTransporterThread(Transportable t){
			this.t = t;
		}

		@Override
		public void run() {
			t.initCommunicationChannel();
			t.transportPages();
		}
	}
}
